package com.bluerox.updating;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

/** 
* XML类型文件的工具类<br><br>
* Example:<br><br>
* 解析XML文件<br><br>
* String xmlFileName = "/home/lijianbo/test/race/temp/status/1234567890abcdef.xml";<br>
* Document xmlDocument = XML.parseFile(xmlFileName);<br><br>
* 保存XML文件<br><br>
* Entity entity = new Entity();<br>
* entity.setTitle("race");<br>
* entity.setVersion("1.0.0");<br>
* ...<br><br>
* 
* XML xml = new XML(entity);<br>
* String path = "/home/lijianbo/test/race/temp/status/29d83234234667.xml";<br>
* xml.save(xmlDocument, path);<br>
* @author kivenLi 
*/
public class XML {
	
	private Entity entity = null;
	
	public XML (Entity entity){
		this.entity = entity;
	}
	
	/**
	 * 保存文档对象到指定文件路径<br>
	 * Example:<br><br>
	 * 解析XML文件<br><br>
	 * String xmlFileName = "/home/lijianbo/test/race/temp/status/1234567890abcdef.xml";<br>
	 * Document xmlDocument = XML.parseFile(xmlFileName);<br><br>
	 * 保存XML文件<br><br>
	 * Entity entity = new Entity();<br>
	 * entity.setTitle("Race");<br>
	 * entity.setVersion("1.0.0");<br>
	 * ...<br><br>
	 * 
	 * XML xml = new XML(entity);<br>
	 * String path = "/home/lijianbo/test/race/temp/status/29d83234234667.xml";<br>
	 * xml.save(xmlDocument, path);<br>
	 * @param doc 文档对象
	 * @param path 指定文件路径
	 */
	public void save(Document doc, String path){

		//备份原有状态文件
		File oldStatusFile = new File(this.entity.getTempStatusPath() + File.separator + this.entity.getStatusFileName());
		File statusFileBackUp = new  File(this.entity.getTempStatusPath() + File.separator + "Status.Back");
		
		oldStatusFile.renameTo(statusFileBackUp);
		
		XMLOutputter outp = new XMLOutputter(Format.getPrettyFormat());
		
		try {
			/*
			 * 输出XML文档,将修改的xml文档以_oldStatusFile的文件命保存到原来的路径下，原先的那个xml文件已经被重命名为Status.Back了
			 * 也就是说，此时的状态文件与未备份的状态文件虽然名字相同，但是，文件如何存在和保存的规则却不一样，更为不一样的是文件内的内容。
			 */
			outp.output(doc, new FileOutputStream(path));
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (IOException e1) {
			e1.printStackTrace();
		} 
		
		//获得文件MD5作为状态文件的新文件名
		String newStatusFileName = "";
		try {
			newStatusFileName = MD5.getHash(this.entity.getTempStatusPath() + File.separator + this.entity.getStatusFileName(), "MD5") + ".xml";
		} catch (Exception e1) {
			e1.printStackTrace();
		}
		File newStatusFile = new File(this.entity.getTempStatusPath() + File.separator + newStatusFileName);
		this.entity.setStatusFileName(newStatusFileName);
		
		
		/* Debug by kiven 2010-10-19 09:48:54
		 * 
		 * 下面的两行代码已经没有用处了，因为在第一次对oldStatusFile进行renameTo("Status.Back")操作是，oldStatusFile已经不存在了
		 * 
		 * 但是要注意的是：此时的oldStatusFile指向的虽然是同一个索引，但是，该索引的值已经变了...一定要弄清楚
		 */
		oldStatusFile.renameTo(newStatusFile);
		oldStatusFile.delete();
	}
	
	/**
	 * 解析XML文件<br><br>
	 * Example:<br><br>
	 * String xmlFileName = "/home/lijianbo/test/race/temp/status/1234567890abcdef.xml";<br>
	 * Document xmlDocument = XML.parseFile(xmlFileName);<br><br>
	 * @param fileName 带路径的文件名
	 * @return XML文件的文档对象
	 */
	public static Document parseFile(String fileName) {
		//得到Document，我们以后要进行的所有操作都是对这个Document操作的
		Document doc = null;
		//使用JDOM首先要指定使用什么解析器,这里表示用默认构造器
		SAXBuilder builder = new SAXBuilder(false);
		try {
			/*
			 * Modify by kiven 2010-10-25 16:18:19 pm
			 * 
			 * Bug!!!!_夸平台的Bug！这条语句使得该程序只能在PC下运行，而无法在Android下运行的原因。
			 * doc = builder.build(fileName);
			 * 改成
			 * doc = builder.build(new FileInputStream(fileName));
			 */
			doc = builder.build(new FileInputStream(fileName));
			
		} catch (JDOMException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return doc;
	}
	
	/**
	 * 解析状态文件后得到所有需下载文件的大小<br><br>
	 * @param doc 要解析的xml文件所对应的Document对象
	 * @return 返回文件所需要下载的大小，单位为KB
	 */
	public int getDownloadFileSize(Document doc) {
		int downloadFileSize = 0;
		//取得索引文件中所有<level>结点
		List list = doc.getRootElement().getChildren("level");
		Iterator iter = list.iterator();
		
		Element element = null;
		while(iter.hasNext()) {
			//取出所有的level节点
			element = (Element)iter.next();
			//取出level下的所有节点
			List md5List = element.getChildren();
			Iterator md5Iter = md5List.iterator();
			//存放level下的节点
			Element md5Element = null;
			//结点中是否有size属性值
			String sizeValue = null;
				while(md5Iter.hasNext()) {
					md5Element = (Element)md5Iter.next();
					//取出有size属性的节点的属性值，有size属性的文件都是要下载的文件
					if((sizeValue = md5Element.getAttributeValue("size")) != null) {
						downloadFileSize += Integer.parseInt(sizeValue);
					}
				}
		}
		return downloadFileSize;
	}
	
	/**
	 * 获得已经下载的文件的大小<br><br>
	 * @param doc 要解析的xml文件所对应的Document对象
	 * @return 返回文件已经下载了的大小
	 */
	public int getHasDownloadSize(Document doc) {
		int hasDownloadSize = 0;
		
		//获得状态索引文件中所有<level>节点
		List list = doc.getRootElement().getChildren("level");
		Iterator iter = list.iterator();
		
		Element element = null;
		//循环取出状态索引文件中所有<level>节点
		while(iter.hasNext()) {
			//取出<level>节点
			element = (Element)iter.next();
			//取出<level>节点下的所有节点
			List md5List = element.getChildren();
			Iterator md5Iter = md5List.iterator();
			
			//存放<level>节点的子节点
			Element md5Element = null;
			//存放<level>节点下字节点的status属性
			String statusValue = null;
			while(md5Iter.hasNext()) {
				md5Element = (Element)md5Iter.next();
				statusValue = md5Element.getAttributeValue("status");
				if(statusValue.equals("download")) {
					//取得属性status=download的size的大小。
					hasDownloadSize += Integer.parseInt(md5Element.getAttributeValue("status"));
				}
			}
		}
		return hasDownloadSize;
	}
	
	/**
	 * 获得状态文件所指定级别中的<status>对应的值
	 * @param doc 解析的xml文件所对应的Document对象
	 * @param val 级别值
	 * @return 返回所传入级别值节点下所对应的<status>节点值
	 */
	public String getStatusByLevelValue(Document doc ,String val) {
		String statusValue = null;
		//获取索引文件中所有的<level>节点
		List list = doc.getRootElement().getChildren("level");
		Iterator iter = list.iterator();
		
		//存放level节点
		Element element = null;
		//临时存放所得的value属性值
		String tempStatusValue = null;
		while(iter.hasNext()) {
			element = (Element)iter.next();
			tempStatusValue = element.getAttributeValue("value");
			if(tempStatusValue.equals(val)) {
				statusValue = element.getChildText("status");
			}
		}
		return statusValue;
	}
	
	/**
	 * 设定状态文件指定界别下的<status>值为download
	 * @param doc
	 * @param levelValue
	 */
	public void setStatusByLevelValue(Document doc,String levelValue,String statusValue) {
		//获取索引文件中所有的<level>节点
		List list = doc.getRootElement().getChildren("level");
		Iterator iter = list.iterator();
		
		//存放level节点
		Element element = null;
		//临时存放所得的value属性值
		String tempStatusValue = null;
		while(iter.hasNext()) {
			element = (Element)iter.next();
			tempStatusValue = element.getAttributeValue("value");
			if(tempStatusValue.equals(levelValue)) {
				element.getChild("status").setText(statusValue);
			}
		}
	}
	
	/**
	 * 获得状态文件中的版本号<br><br>
	 * @param doc 要解析的xml文件所对应的Document对象
	 * @return 返回版本号，这里的版本号类型为String类型
	 */
	public String getVersion(Document doc) {
		return doc.getRootElement().getChild("version").getText();
	}
	
	/**
	 * 构建状态文件<br>
	 * Example<br>
	 * sourcePath = "/var/lib/tomcat6/webapps/ROOT/race_test/race1.0.0/level1"<br>
	 * destPath = "/var/lib/tomcat6/webapps/ROOT/race_test/race1.0.0"<br>
	 * level = "1"<br>
	 * xmlName = "status.xml"<br>
	 * @param sourcePath 资源文件所存放的地址
	 * @param destPath	存放状态文件的地址
	 * @param levelValue	该资源文件所对应的状态级别
	 * @param xmlName  生成xml文件的名字
	 */
	public void buildStatusFile(String sourcePath,String destPath,String levelValue,String xmlName) {
		File file = new File(sourcePath);
		//获得该资源文件目录下的所有文件,不过该文件必须是.zip类型的文件
		File[] files = file.listFiles(new FilenameFilter() {
			@Override
			public boolean accept(File dir, String name) {
				return name.endsWith(".zip");
			}
		});
		
		//级别为1时，状态文件还没存在，要先进行创建。
		if(levelValue.equals("1")) {
			//创建状态xml文档
			//第一步，创建根元素，因为一个xml文档必须一直有一个唯一的根元素，所以Document将Element放在它的构造器中。
			Element rootElement = new Element("files");
			Document statusDocument = new Document(rootElement);
			//第二步，为根元素添加属性，状态文件的根元素无属性，则跳过此步骤
			//第三步，添加其他子元素
			//添加<version>1.0.0</version>
			rootElement.addContent(new Element("version").addContent(this.entity.getVersion()));
			//添加<top-status>pending</top-status>
			rootElement.addContent(new Element("top-status").addContent("pending"));
			//添加<level value="1"></level>
			rootElement.addContent(new Element("level").setAttribute("value",levelValue));
			//将此时的配置文件保存起来，这么做的目的是方便操作level节点下加入文件
			XMLOutputter outputter = new XMLOutputter();
			try {
				outputter.output(statusDocument, new BufferedWriter(new FileWriter(destPath + File.separator + xmlName)));
			} catch(IOException e) {
				e.printStackTrace();
			}
			//保存了之后就对该xml文件进行操作，先得到该文件的Document对象
			Document doc = XML.parseFile(destPath + File.separator + xmlName);
			//得到了document对象就可以获得level节点，往<level value="1"></level>里面添加相应的内容
			Element levelElement = doc.getRootElement().getChild("level");
			//添加<status></status>节点
			levelElement.addContent(new Element("status").addContent("pending"));
			
			//将该级别下的所有文件所对应的MD5码写到配置文件中
			for(File f : files) {
				//文件的对应MD5码
				try {
					String fileMD5 = MD5.getHash(f.getAbsolutePath(), "MD5");
					levelElement.addContent(new Element("md5-" + fileMD5).setAttribute("status", "pending").setAttribute("size",(f.length()+"")));
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			
			//添加完成之后保存文件
			try {
				Format fmt = Format.getPrettyFormat();
				//缩进的长度
				fmt.setIndent("	");
				outputter.setFormat(fmt);
				outputter.output(doc, new BufferedWriter(new FileWriter(destPath + File.separator + xmlName)));
			} catch(Exception e) {
				e.printStackTrace();
			}
		}
		//级别不为1的情况，此时状态文件已经存在，只需要修改原先的状态文件，将该级别下的所有文件加进去即可
		else {
			//得到已存在的xml文件的Document对象
			Document doc = XML.parseFile(destPath + File.separator + xmlName);
			//添加level节点
			Element levelElement = new Element("level");
			//设定level节点的属性
			levelElement.setAttribute("value",levelValue);
			//为level节点添加<status>子节点
			levelElement.addContent(new Element("status").addContent("pending"));
			
			//将该级别下的所有文件所对应的MD5码写到配置文件中
			for(File f : files) {
				//文件的对应MD5码
				try {
					String fileMD5 = MD5.getHash(f.getAbsolutePath(), "MD5");
					levelElement.addContent(new Element("md5-" + fileMD5).setAttribute("status", "pending").setAttribute("size",(f.length()+"")));
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			
			//添加的已有的xml文件中
			doc.getRootElement().addContent(levelElement);
			//添加完成之后保存文件
			try {
				XMLOutputter outputter = new XMLOutputter();
				Format fmt = Format.getPrettyFormat();
				//缩进的长度
				fmt.setIndent("	");
				outputter.setFormat(fmt);
				outputter.output(doc, new BufferedWriter(new FileWriter(destPath + File.separator + xmlName)));
			} catch(Exception e) {
				e.printStackTrace();
			}
			
		}
		
	}
	
	/**
	 * 构建最基础的索引文件<br>
	 * Example<br><br>
	 * String destPath ="/var/lib/tomcat6/webapps/ROOT/race/race1.0.0"<br>
	 * int maxLevel = 1;
	 * String mainFileName = "race_level_1.xml"; 
	 * @param destPath	存放索引文件的地址
	 * @param maxLevel	索引文件的最大级别
	 * @param mainFileName	索引文件的名称
	 */
	public void buildBaseMainFile(String destPath,int maxLevel,String mainFileName) {
		//构建xml文件
		//创建根节点
		Element rootElement = new Element("program");
		//创建索引文件的Document对象
		Document mainDocument = new Document(rootElement);
		//添加<title>race</title>节点
		rootElement.addContent(new Element("title").addContent(this.entity.getTitle()));
		//添加<version>1.0.0</version>节点
		rootElement.addContent(new Element("version").addContent(this.entity.getVersion()));
		
		//maxLevel>0时，表示创建的是主索引文件，maxLevel=0时，表示的是创建次索引文件
		if(maxLevel > 0) {
			//索引文件中<max-level></max-level>节点，只有在主索引文件中存在
			rootElement.addContent(new Element("max-level").addContent(maxLevel+""));
		}
		
		//添加<root></root>节点
		rootElement.addContent(new Element("root").setAttribute("name", this.entity.getTitle()));
		//此时状态文件的组要结构已经创建完成，为了操作的方便，现将此文件保存起来
		try {
			XMLOutputter outputter = new XMLOutputter();
			Format fmt = Format.getPrettyFormat();
			//缩进的长度
			fmt.setIndent("	");
			outputter.setFormat(fmt);
			outputter.output(mainDocument, new BufferedWriter(new FileWriter(destPath + File.separator + mainFileName)));
		} catch(IOException e) {
			e.printStackTrace();
		}
	}
	/**
	 * 构建索引文件<br>
	 * Example<br><br>
	 * String rootPath = "/home/lijianbo/testSourceFiles"; <br>
	 * String mainFilePath = "/var/lib/tomcat6/webapps/ROOT/race/race1.0.0/race_level_1.xml";<br>
	 * Element element = XML.parseFile(mainFilePath).getRootElement().getChild("root");//初始状态的节点是索引文件中的<root>节点<br><br>
	 * 
	 * @param rootPath 资源文件的根目录
	 * @param mainFilePath	该索引文件的存放路径
	 * @param element 状态文件中的节点 
	 */
	public void buildMainFile(String rootPath,String mainFilePath,Element element) {
		//创建资源文件路径下的File类
		File file = new File(rootPath);
		//得到该资源文件路径下的所有文件
		File[] files = file.listFiles();
		Element ele = null;
		
		//循环递归的方式写索引文件
		for(File f : files) {
			//f有两种情况;
			//1.f是文件夹的情况  
			if(f.isDirectory()) {
				//将该节点保存到<root>的节点中
				element.addContent(new Element("directory").setAttribute("name", f.getName()));
				
				
				XMLOutputter outputter = new XMLOutputter();
				Format fmt = Format.getPrettyFormat();
				//缩进的长度
				fmt.setIndent("	");
				outputter.setFormat(fmt);
				try {
					outputter.output(XML.parseFile(mainFilePath), new BufferedWriter(new FileWriter(mainFilePath)));
				} catch (IOException e) {
					e.printStackTrace();
				}
				
				ele = element.getChild("directory");
				
				//测试
				System.out.println("******");
				System.out.println(ele.getName());
				System.out.println(ele.getAttributeValue("name"));
				System.out.println("******");
				//定义一个递归函数
				buildMainFile(f.getAbsolutePath(),mainFilePath,ele);
			}
			//2.f是文件的情况
			if(!f.isDirectory()) {
				try {
					//测试
					System.out.println("########");
					System.out.println(element.getName());
					System.out.println(element.getAttributeValue("name"));
					System.out.println("########");
					
					element.addContent(new Element("file").setAttribute("name",f.getName() + Entity.ZIP_EXT)).addContent(new Element("md5")
					.addContent(MD5.getHash(new File(mainFilePath).getParent() + File.separator + "level" + mainFilePath.substring(mainFilePath.length()-5, mainFilePath.length()-4)
							+ File.separator + f.getName() + Entity.ZIP_EXT, "MD5")));
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			
		}
		
		//保存该索引文件
		//?
		try {
			XMLOutputter outputter = new XMLOutputter();
			Format fmt = Format.getPrettyFormat();
			//缩进的长度
			fmt.setIndent("	");
			outputter.setFormat(fmt);
			outputter.output(XML.parseFile(mainFilePath), new BufferedWriter(new FileWriter(mainFilePath)));
		} catch(IOException e) {
			e.printStackTrace();
		}
		
	}
}
